package moonshine
{
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	
	public class EditManager extends EventDispatcher
	{
		public var model:TextEditorModel;
		public var selectionManager:SelectionManager;
		public var editor:TextEditor;
		
		
		public function handleKeyDown(event:KeyboardEvent):void
		{
			switch (event.keyCode)
			{
				default:
				{
					var c:String = String.fromCharCode(event.charCode)
					insert(c);
					break;
				}	
				case Keyboard.ENTER:
				{
					insertLineAtCursor();
					break;
				}
				case Keyboard.BACKSPACE:
				{
					removeAtCursor();
					break;
				}
				case Keyboard.DELETE:
				{
					removeAtCursor(true);
				}
				case Keyboard.DOWN:
				case Keyboard.UP:
				case Keyboard.LEFT:
				case Keyboard.RIGHT:
				{
					selectionManager.handleKeyDown(event);
					break;
				}
				case Keyboard.SHIFT:
				case Keyboard.CONTROL:
				case Keyboard.COMMAND:
				case Keyboard.ALTERNATE:
				{
					break;
				}
				
			}
		}

		public function insert(s:String):void
		{
			var rdr:TextLineRenderer = editor.getRendererFromIndex( model.selectedLineIndex );
			if (!rdr) return;
			
			if (model.hasSelection) removeSelection();
			
			var line:String = model.lines[model.selectedLineIndex];
			var p:int = model.caretIndex;
			model.lines[model.selectedLineIndex] = line.substring(0, p) + s + line.substring(p);
			model.caretIndex++;
			
			editor.invalidateLines();
		}
		
		public function removeAtCursor(afterCaret:Boolean=false):void
		{
			var rdr:TextLineRenderer = editor.getRendererFromIndex(model.selectedLineIndex);
			var lineLength:int = rdr.data.length;
			
			if (model.hasSelection) {
				removeSelection();
				editor.invalidateLines();
				return;
			}
			
			// Backspace remove line & append to line above it
			if (model.caretIndex == 0 && !afterCaret)
			{
				// Can't remove first line with backspace
				if (model.selectedLineIndex == 0) return;
				
				model.selectedLineIndex--;
				model.caretIndex = model.selectedLine.length;
				
				model.lines[model.selectedLineIndex] += rdr.data;
				removeline(model.selectedLineIndex+1);
			}
			// Delete remove linebreak & append to line below it
			else if (model.caretIndex == lineLength && afterCaret)
			{
				if (model.selectedLineIndex == model.lines.length-1) return;
				
				model.selectedLine += model.lines[model.selectedLineIndex+1];
				
				removeline(model.selectedLineIndex+1);
			}
			else if (afterCaret) // Delete
			{
				rdr.data = rdr.data.slice(0, model.caretIndex) + rdr.data.substring(model.caretIndex+1);
				model.lines[model.selectedLineIndex] = rdr.data;	
			} 
			else // Backspace
			{
				rdr.data = rdr.data.slice(0, model.caretIndex-1) + rdr.data.substring(model.caretIndex);
				model.lines[model.selectedLineIndex] = rdr.data;
				model.caretIndex--;
			}
			
			editor.invalidateLines();
		}
		
		private function removeline(lineIndex:int):void
		{
			var rows:Array = model.lines;
			model.lines = rows.slice(0, lineIndex).concat(rows.slice(lineIndex+1));
			
			editor.invalidateLines();
		}
		
		private function insertLineAtCursor():void
		{
			if (model.hasSelection) removeSelection();
			
			var trailing:String = model.selectedLine.substring(model.caretIndex);
			model.lines[model.selectedLineIndex] = model.selectedLine.substring(0, model.caretIndex); 
			
			model.caretIndex = 0;
			insertLine(++model.selectedLineIndex, trailing);
		}
		
		private function insertLine(afterLineIndex:int, s:String):void
		{
			var rows:Array = model.lines;
			model.lines = rows.splice(0, afterLineIndex).concat([s]).concat(rows);
			editor.invalidateLines();
		}
		
		private function removeSelection():void
		{
			var line:String;
			if (model.hasMultilineSelection)
			{
				var startChar:int, endChar:int, startLine:int, endLine:int;
				if (model.selectionStartLineIndex < model.selectedLineIndex)
				{
					startChar = model.selectionStartCharIndex;
					endChar = model.caretIndex;
					startLine = model.selectionStartLineIndex;
					endLine = model.selectedLineIndex;
				}
				else
				{
					startChar = model.caretIndex;
					endChar = model.selectionStartCharIndex;
					startLine = model.selectedLineIndex;
					endLine = model.selectionStartLineIndex;
				}
				 
				// Save trailing chars of last selected line
				var trailing:String = model.lines[endLine].substring(endChar);
				
				// Snip start line & append trailing line
				line = model.lines[startLine];
				line = line.substring(0, startChar);
				model.lines[startLine] = line + trailing;
				removeline(endLine);
				
				// Remove lines inbetween
				for (var i:int = model.selectionStartLineIndex+1; i < model.selectedLineIndex; i++) 
				{
					removeline(i);
				}

				model.selectedLineIndex = startLine;
				model.caretIndex = startChar;
			}
			else
			{
				line = model.selectedLine;
				var start:int 	= Math.min(model.selectionStartCharIndex, model.caretIndex);
				var end:int 	= Math.max(model.selectionStartCharIndex, model.caretIndex);
				model.selectedLine = line.substring(0, start) + line.substring(end);
				model.caretIndex = start;
			}
			
			selectionManager.removeSelection();
			editor.invalidateSelection();
		}
	}
}